﻿using System.Linq;
using Daemon.Common;
using Daemon.Common.Helpers;
using Daemon.Infrustructure.EF;
using Daemon.Repository.EF.Enums;
using Daemon.Repository.Contract;
using Daemon.Model;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using Daemon.Data.Substructure.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;

namespace Daemon.Repository.EF
{
    public class UserSessionRepository : Repository<UserSession>, IUserSessionRepository
    {
        private readonly IHttpContextAccessor _httpContext;

        protected override Expression<Func<UserSession, int>> PrimaryKeyExpression => r => r.Id;

        public UserSessionRepository(ApiDBContent context, IHttpContextAccessor httpContext) : base(context)
        {
            _httpContext = httpContext;
        }

        public void SetLogoutTimeByUser(int userId, LogoutReasonEnum logoutReason, ApiDBContent context = null)
        {
            context = context ?? Context;
            var sqlOfUpdateLogoutReason = $@"
				UPDATE	user_session
				SET		user_session.LogoutReasonId = 0,
						user_session.LogoutTime = '{DateTime.Now.ToString("yyyy-MM-dd HH:MM:ss")}'
				where Id  in	( select Id from	(select user_session.Id from user_session inner join (SELECT t1.UserId FROM (
     SELECT * FROM user_session  WHERE UserId = {userId} and LogoutTime is null ORDER BY LoginTime ASC
) AS t1 GROUP BY UserId limit 1) as s on s.UserId = user_session.UserId)  AS  result);
";
            Context.Database.ExecuteSqlRaw(sqlOfUpdateLogoutReason);
        }

        public void UpdateLogoutTimeForSession(List<int> userIds, LogoutReasonEnum logoutReason)
        {
            if (userIds.Count == 0)
            {
                return;
            }

            var sql = $@"SELECT  t1.*
                         FROM user_session t1
                         JOIN (
                           SELECT UserId, max(LoginTime) as min_order
                           FROM user_session
                           GROUP BY UserId
                         ) t2 ON t1.UserId = t2.UserId AND t1.LoginTime = t2.min_order where LogoutTime is null and t1.UserId in({string.Join(",", userIds)})";
            var sessionsOfShouldExpired = Context.UserSession.FromSqlRaw(sql).ToList();

            if (sessionsOfShouldExpired.Count > 0)
            {
                sessionsOfShouldExpired.ForEach(session =>
                {
                    session.LogoutTime = DateTime.UtcNow;
                    session.LogoutReasonId = (short)logoutReason;
                });

                Context.SaveChanges();
            }
        }

        public void AddNewSessionIfNoLive()
        {
            if (GetDbQuery().Any(s => s.UserId == GetUserId() && s.LogoutTime.HasValue))
            {
                return;
            }

            var session = new UserSession()
            {
                UserId = GetUserId(),
                IP = ipAddress(),
                LoginTime = DateTime.UtcNow,
            };

            Add(session);
        }

        protected override void UpdateEntitiesBeforeCreate(IEnumerable<UserSession> entities, ApiDBContent context)
        {
            if (AuthInfo == null)
            {
                return;
            }

            foreach (var entity in entities)
            {
                entity.UserId = AuthInfo.UserId;
            }
        }

        protected override void UpdateEntitiesBeforeUpdate(IEnumerable<UserSession> entities, ApiDBContent context)
        {
            UpdateEntitiesBeforeCreate(entities, context);
        }

        private string ipAddress()
        {
            if (_httpContext.HttpContext.Request.Headers.ContainsKey("X - Forwarded - For"))
                return _httpContext.HttpContext.Request.Headers["X-Forwarded-For"];
            else
                return _httpContext.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
        }
    }
}
